<?php
/**
 * 此题无解 中转输入脚本 测试版
 *
 * @category   中转类
 * @author      此题无解
 * 更多Web安全交流 Q群：260044099
 */
/*
//输入部分
[method] => Get
[url] => 
[header] => 
[injectparam] => 
[injectvalue] =>
[postparam] =>
//处理部分


*/
include_once("common.php");
include_once("method_ext.php");   //额外的中转规则
include_once("encode.php");       //编码规则

/**
 *  中转类
 */
class Transform {

	public  $loadok = false;
	public  $errorinfo = '';
	public  $config = array();
	public 	$REPLACETAG = '';
	public 	$PackageChecked = array();
	public 	$php_keywords = array();

    function __construct($configxml, $theme) {
    	//echo $configxml;
    	$this->REPLACETAG = '[ctwj]';
    	array_push($this->PackageChecked, 'user-agent','cookie');
    	array_push($this->php_keywords, 'select','from','union','order','group','having','and','or');
    	if (file_exists($configxml)) {
    		$xml = simplexml_load_file($configxml);
    		$path = '/TransFormConfig/'.$theme;
    		$xmlconfig = $xml->xpath($path);
    		$this->config = (array)$xmlconfig[0];
    		$this->AnalysisParam();
    	} else $this->errorinfo="Load config error!";
    }

/**
 * [AnalysisParam 检测载入的配置是否有效]
 */
    private function AnalysisParam() {
    	if (is_array($this->config)){
    		$result = CheckVaild($this->config, $this->REPLACETAG, $this->PackageChecked);
    		if ($result !== True)
    		{
    			$this->errorinfo = $result['info'];
    			return false;
    		} else return true;
    	} else {
    		$this->errorinfo = 'Load config error!';
    		return false;
    	}
    }


    /**
     * [transform 中转参数]
     * @param  [type] $id [中转值]
     * @return [type]     [description]
     * 
     * 首先，我需要什么？
     * 0. 包或url
     * 1. 中转参数
     * 2. 中转规则
     * 3. 中转编码
     *
     * 过程 ： 如果是伪静态，直接跳过中转规则，调用编码函数，然后发包。
     * 如果不是伪静态，先将中转参数从原数据中删除，再根据中转规则添加到数据包中
     * 再编码发包。
     */
    public function transform($id){
    	$isPackage = array_key_exists('header', $this->config);
		$isFakeUrl = array_key_exists('fakeurl', $this->config);
		$isPost = in_array('Post', $this->config);
		$isMothodExt = array_key_exists('method_ext', $this->config);

		$encodeResult = $this->EncodeParam($id, $this->php_keywords);  //参数编码

		if ($isMothodExt){
			$this->config['method_ext']($this->config);
		} else {
			if ($isPackage) {
				$this->SendPackage($encodeResult);
			} else {
				$this->SendUrl($encodeResult);
			}
		}
    }


    /**
     * [SendPackage 转发包类型的数据]
     * @param [type] $encodeResult [编码的参数]
     */
    private function SendPackage($encodeResult){
		$isPackage = array_key_exists('header', $this->config);
		$isPost = in_array('Post', $this->config);
		//伪静态的提交
		//$headers['CLIENT-IP'] = '202.103.229.40';
		//$headers['X-FORWARDED-FOR'] = '202.103.229.40'; 随机伪ip
		$analysis = $this->AnalysisPackage($encodeResult, false);
		//inject       需要中转的参数
		//url          提交的地址
		//header       头信息已经去掉Content-Length
		//type         默认提交的类型
		//default_pos  中转参数默认的位置
		//post         要Post的数据

		if ($this->config['method_way'] == 'default')$this->config['method_way'] = $analysis['default_pos'];
		//根据中转规则来添加中转参数
		switch (strtolower($this->config['method_way'])) {
		 	case 'get':
		 		$analysis['url'] = strpos($analysis['url'], '?')?$analysis['url'].'&'.$analysis['inject']:$analysis['url'].'?'.$analysis['inject'];
		 		break;
		 	case 'post':
		 		$analysis['type'] = 'POST';
		 		$analysis['post'] = $analysis['post'].'&'.$analysis['inject'];
		 		$analysis['header'][] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8';
		 		 break;
		 	case 'cookie':
		 		$add = false;
		 		for ($i=0; $i < count($analysis['header']); $i++) { 
		 			$info= explode(':', $analysis['header'][$i]);
		 			if (strtolower($info[0]) == 'cookie') {
		 				$analysis['header'][$i] .=';'.$analysis['inject'];
		 				$add = true;break;
		 			}
		 		}
		 		if (!$add){
		 			$analysis['header'][] = 'Cookie: '.$analysis['inject'];
		 		}
		 		break;
		 	case 'multi':
		 		$file = realpath('./1.jpg');
				$fields['f'] = '@'.$file;
				$pdata = explode('=', $analysis['inject']);
				$multiData = array(
					'file' => '@'.$file,
					$pdata[0] => $pdata[1],
				);
				//如果是原始是post方式提交的，把数据也放到这来
				if (isset($analysis['post'])) {
					$edata = explode('&', $analysis['post']);
					for ($i=0; $i < count($edata)-1; $i++) { 
						if (trim($edata[$i]) != '') {
							$dataexplode = explode('=', $edata[$i]);
							if (count($dataexplode) == 2) {
								$multiData[$dataexplode[0]] = $dataexplode[1];
							}
						}
					}
				}
		 		break;
		 	default:
		 		# code... 都不是就在header里面,应该不会存在这种情况
		 		break;
		}

		$ch = curl_init($analysis['url']);
		//post
		if ($analysis['type'] == 'POST' || $this->config['method_way'] == 'multi'){
			curl_setopt($ch, CURLOPT_POST, 1);
			if (strtolower($this->config['method_way']) != 'multi') {
				curl_setopt($ch, CURLOPT_POSTFIELDS, $analysis['post']);
			} else {
				curl_setopt($ch, CURLOPT_POSTFIELDS, $multiData);
			}
		}

		if (DEBUG) {
			echo "<pre>";
			echo 'method_way->'.$this->config['method_way'].'<br>';
			echo 'default->'.$analysis['default_pos'].'<br>';
			//echo "multi>";print_r($fields);
			print_r($analysis);
			echo "</pre>";
			curl_setopt($ch,CURLOPT_PROXY,'127.0.0.1');
			curl_setopt($ch,CURLOPT_PROXYPORT,'8080');
			//curl_setopt($ch, CURLOPT_HEADER, 1);    //输出返回的头信息
		}

		curl_setopt($ch, CURLOPT_REFERER, $analysis['url']); //REFER
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER , $analysis['header']);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

		$output = curl_exec($ch);
		curl_close($ch);
		echo $output;
    }

    private function SendUrl($encodeResult){
    	$isPackage = array_key_exists('header', $this->config);
		$isPost = in_array('Post', $this->config);
		$isFakeUrl = array_key_exists('fakeurl', $this->config);
		$encodeResult = urlencode($encodeResult);
		if ($isFakeUrl) {
			$analysis = $this->AnalysisUrl($encodeResult, true);
		} else $analysis = $this->AnalysisUrl($encodeResult, false);

		//根据中转规则来添加中转参数
		if ($this->config['method_way'] == 'default') {
			if ($isPost) $this->config['method_way'] = 'POST';
			else $this->config['method_way'] = 'GET';
		}
		switch (strtolower($this->config['method_way'])) {
		 	case 'get':
		 		$analysis['header'][] = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0';
		 		$analysis['url'] = strpos($analysis['url'], '?')?$analysis['url'].'&'.$analysis['inject']:$analysis['url'].'?'.$analysis['inject'];
		 		break;
		 	case 'post':
		 		$analysis['type'] = 'POST';
		 		$analysis['post'] = $analysis['post'].'&'.$analysis['inject'];
		 		$analysis['header'][] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8';
		 		 break;
		 	case 'cookie':
		 		$analysis['header'][] = 'Cookie: '.$analysis['inject'];
		 		break;
		 	case 'multi':
		 		$file = realpath('./1.jpg');
				$fields['f'] = '@'.$file;
				$pdata = explode('=', $analysis['inject']);
				$multiData = array(
					'file' => '@'.$file,
					$pdata[0] => $pdata[1],
				);
				//如果是原始是post方式提交的，把数据也放到这来
				if (isset($analysis['post'])) {
					$edata = explode('&', $analysis['post']);
					for ($i=0; $i < count($edata)-1; $i++) { 
						if (trim($edata[$i]) != '') {
							$dataexplode = explode('=', $edata[$i]);
							if (count($dataexplode) == 2) {
								$multiData[$dataexplode[0]] = $dataexplode[1];
							}
						}
					}
				}
		 		break;
		 	default:
		 		# code... 都不是就在header里面,应该不会存在这种情况
		 		break;
		}

    	$ch =curl_init($analysis['url']);
    	//post
		if (@$analysis['type'] == 'POST' || $this->config['method_way'] == 'multi'){
			curl_setopt($ch, CURLOPT_POST, 1);
			if (strtolower($this->config['method_way']) != 'multi') {
				curl_setopt($ch, CURLOPT_POSTFIELDS, $analysis['post']);
			} else {
				curl_setopt($ch, CURLOPT_POSTFIELDS, $multiData);
			}
		}
		if (DEBUG) {
			echo "<pre>";
			echo 'method_way->'.$this->config['method_way'].'<br>';
			echo 'default->'.$analysis['default_pos'].'<br>';
			//echo "multi>";print_r($fields);
			print_r($analysis);
			echo "</pre>";
			curl_setopt($ch,CURLOPT_PROXY,'127.0.0.1');
			curl_setopt($ch,CURLOPT_PROXYPORT,'8080');
			//curl_setopt($ch, CURLOPT_HEADER, 1);    //输出返回的头信息
		}

		curl_setopt($ch, CURLOPT_REFERER, $analysis['url']); //REFER
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER , $analysis['header']);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

		$output = curl_exec($ch);
		curl_close($ch);
		echo $output;
    }


    /**
     * [EncodeParam 编码参数]
     * @param [type] $id [description]
     */
    private function EncodeParam($id , $keyparam){
    	if (array_key_exists('encode_way', $this->config)) {
    		if (function_exists($this->config['encode_way']))return $this->config['encode_way']($id,$keyparam);
    		else return $id;
    	} else return $id;
    }

    /**
     * [AnalysisUrl 分析URL]
     * @param [type] $encodeResult [已经编码的参数，只传递]
     * @param [type] $fakeurl      [是不是伪静态]
     */
    private function AnalysisUrl($encodeResult, $fakeurl){
    	$result = array();
    	$isPost = in_array('Post', $this->config);
    	//$encodeResult = urlencode($encodeResult);
    	//需要处理的就只有url和 postparam
    	if ($fakeurl) {
    		$result['url'] = str_replace($this->REPLACETAG, $encodeResult, $this->config['url']);
    		if ($isPost) {
    		//处理POST数据
    		$result['post'] = str_replace($this->REPLACETAG, $encodeResult, $this->config['postparam']);
    		}
    	} else {
    		$result['inject'] = $this->config['injectparam'].'='.$encodeResult;
    		$pattern = '/'.$this->config['injectparam'].'\s?=\s?'.$this->config['injectvalue'].'/';
      		if (preg_match($pattern, $this->config['url'], $match)){
    			$result['url'] = str_replace($match[0], '', $this->config['url']);
    		}
    		if ($isPost) {
    			if (preg_match($pattern, $this->config['postparam'], $match)){
	    			$result['post'] = str_replace($match[0], '', $this->config['postparam']);
	    		}
    		}
    	}
    	return $result;
    }
    //Content-Length 需要去掉curl会自动计算
    /**
     * [AnalysisPackage 分析包]
     * @param [type] $encodeResult [已经编码的参数，只传递]
     * @param [type] $fakeurl      [是不是伪静态]
     */
    private function AnalysisPackage($encodeResult,$fakeurl) {
    	$requestInfo = array();
    	$header = $this->config['header'];
    	$lines = explode("\n", $header);
    	//分析这个包是什么方式提交的，并提取HOST组合成提交的url
    	preg_match('/^(GET|POST)\s(.*?)\sHTTP\/1/',$lines[0],$match);
    	$type = $match[1];
    	$url = trim($match[2]);
    	for ($i=1; $i <count($lines) ; $i++) {
    		$info = explode(':', $lines[$i]);
    		if ($info[0] == 'Host') {
    			$host = trim($info[1]);
    			break;
    		}
    	}
    	// 已经获取到url host method 开始替换参数
    	$isPostData = false;
    	if ($fakeurl){
    		//如果是伪静态，替换参数
    		$url = str_replace($this->REPLACETAG, $encodeResult, $url);
    		for ($i=1; $i <count($lines) ; $i++) {
    			if (!$isPostData){
    				if (trim($lines[$i]) == ''){
    					$isPostData = True;
    				} else {
    					if (strpos($lines[$i], 'Content-Length') !== false) continue;//去掉Content-Length
    					$headerinfo[] = str_replace($this->REPLACETAG, $encodeResult, $lines[$i]);
    				}
    			} else {
    				$PostInfo[] = str_replace($this->REPLACETAG, $encodeResult, $lines[$i]);
    			}
    		}
    	} else {
    		//如果不是伪静态，去掉参数,放到 ParamData中
    		//先应该找到这个值 这个值 是$config->injectparam
    		$requestInfo['inject'] = $this->config['injectparam'].'='.$encodeResult;
    		//echo $lines[0]."<br>";
    		$pattern = '/'.$this->config['injectparam'].'\s?=\s?'.$this->config['injectvalue'].'/';
      		if (preg_match($pattern, $lines[0], $match)){
    			//如果有，则去掉url中这个参数
    			$url = str_replace($match[0], '', $url);
    			$default_pos = 'get';
    		}
    		for ($i=1; $i <count($lines) ; $i++) {
    			if (!$isPostData){
    				if (trim($lines[$i]) == ''){
    					$isPostData = True;
    				} else {
    					if (strpos($lines[$i], 'Content-Length') !== false) continue;//去掉Content-Length
    					if (strpos($lines[$i], 'Content-Type') !== false) continue;
    					if (!isset($default_pos)){
    						if (preg_match($pattern, $lines[$i], $match)){
    							$headerinfo[] = str_replace($match[0], '', $lines[$i]);
    							$default_pos = explode(':',$lines[$i])[0];
    						} else $headerinfo[] = $lines[$i];
    					} else $headerinfo[] = $lines[$i];
    				}
    			} else {
    				if (!isset($default_pos)){
						if (preg_match($pattern, $lines[$i], $match)){
							$PostInfo[] = str_replace($match[0], '', $lines[$i]);
							$default_pos = 'post';
						} else $PostInfo[] = $lines[$i];
					} else $PostInfo[] = $lines[$i];
    			}
    		}
    	}
    	//print_r($PostInfo);

    	$requestInfo['url'] = $host.$url;
    	$requestInfo['header'] = $headerinfo;
    	$requestInfo['type'] = $type;
    	$requestInfo['default_pos'] = $default_pos;
    	if(isset($PostInfo))$requestInfo['post'] = $PostInfo[0];
    	//echo "<pre>";print_r($requestInfo);die();
    	return $requestInfo;
    }

    /**
     * [HeaderChange 修改头信息，返回修改的序号和值]
     * @param [type] $hlist [列表]
     * @param [type] $param [参数]
     * @param [type] $op    [操作]
     * @param [type] $value [值]
     */
    private function HeaderChange($hlist, $param, $op, $value){

    }


}




?>